/*
   Copyright (c) 2008-2009 LW, Inc. <http://www.lw.com>
   This file is part of LWFS.

   LWFS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 3 of the License,
   or (at your option) any later version.

   LWFS is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see
   <http://www.gnu.org/licenses/>.
*/

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include "lwfs.h"
#include "xlator.h"
#include "defaults.h"
#include "combine.h"

#include <sys/time.h>
#include <libgen.h>

int
combine_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		int op_ret, int op_errno, struct statvfs *statvfs)
{
	combine_local_t *local = NULL;
	int this_call_cnt = 0;

	local = frame->local;
	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			goto unlock;
		}
		local->op_ret = 0;

		local->statvfs.f_bsize    = statvfs->f_bsize;
		local->statvfs.f_frsize   = statvfs->f_frsize;

		local->statvfs.f_blocks  += statvfs->f_blocks;
		local->statvfs.f_bfree   += statvfs->f_bfree;
		local->statvfs.f_bavail  += statvfs->f_bavail;
		local->statvfs.f_files   += statvfs->f_files;
		local->statvfs.f_ffree   += statvfs->f_ffree;
		local->statvfs.f_favail  += statvfs->f_favail;
		local->statvfs.f_fsid     = statvfs->f_fsid;
		local->statvfs.f_flag     = statvfs->f_flag;
		local->statvfs.f_namemax  = statvfs->f_namemax;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		if (!local->op_ret)
			gf_log (this->name, GF_LOG_TRACE,
                                "%"PRId64": ({f_bsize=%lu, f_frsize=%lu, f_blocks=%"GF_PRI_FSBLK
                                ", f_bfree=%"GF_PRI_FSBLK", f_bavail=%"GF_PRI_FSBLK", "
                                "f_files=%"GF_PRI_FSBLK", f_ffree=%"GF_PRI_FSBLK", f_favail=%"
                                GF_PRI_FSBLK", f_fsid=%lu, f_flag=%lu, f_namemax=%lu}) => ret=%d",
                                frame->root->unique, statvfs->f_bsize, statvfs->f_frsize, statvfs->f_blocks,
                                statvfs->f_bfree, statvfs->f_bavail, statvfs->f_files, statvfs->f_ffree,
                                statvfs->f_favail, statvfs->f_fsid, statvfs->f_flag, statvfs->f_namemax, op_ret);
		else
                        gf_log (this->name, GF_LOG_ERROR,
                                "statfs return %"PRId64": (op_ret=%d, op_errno=%d)",
                                frame->root->unique, op_ret, op_errno);

		COMBINE_STACK_UNWIND (statfs, frame, local->op_ret, local->op_errno,
				  &local->statvfs);
	}

        return 0;
}
int
combine_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
                    int op_ret, int op_errno,
                    inode_t *inode, struct stat *stbuf, dict_t *xattr,
                    struct stat *postparent)
{
        call_frame_t *prev                    = NULL;
	combine_conf_t   *conf                = NULL;
        combine_local_t  *local               = NULL;
	int	      is_dir		      = 0;
        int           this_call_cnt           = 0;

	conf  = this->private;
        local = frame->local;
        prev  = cookie;

	LOCK (&frame->lock);
        {
		if (op_ret == -1) {
			local->op_errno = ENOENT;
			gf_log (this->name, GF_LOG_DEBUG,
					"lookup of %s on %s returned error (%s)",
					local->loc.path, prev->this->name,
					strerror (op_errno));

			goto unlock;
		} else
			local->op_ret = op_ret;

		combine_stat_merge (this, &local->stbuf, stbuf, prev->this);

		if (local->inode->ino == 1) {
	  		/* TODO: assert equal mode on stbuf->st_mode and
			 * local->stbuf->st_mode
			 * else mkdir/chmod/chown and fix
			 */

	 		is_dir = check_is_dir (inode, stbuf, xattr);
 			if (!is_dir) {
        	                gf_log (this->name, GF_LOG_DEBUG,
                	                "lookup of %s on %s returned non dir 0%o",
                        	        local->loc.path, prev->this->name,
	                                stbuf->st_mode);
 				goto unlock;
        	        }
	
 			local->op_ret = 0;

			/* first child is primary child */
			if (prev->this == conf->subvolumes[0]) {
				local->st_ino = local->stbuf.st_ino;
                        	local->st_dev = local->stbuf.st_dev;
	                }
		} else 
			combine_itransform (this, prev->this, stbuf->st_ino,
				&stbuf->st_ino);
        }
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {

		COMBINE_STACK_UNWIND (lookup, frame, local->op_ret, local->op_errno,
				  local->inode, &local->stbuf, NULL, NULL);
	}
	return 0;
}

int
combine_attr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		int op_ret, int op_errno, struct stat *stbuf)
{
	combine_local_t *local = NULL;
	call_frame_t *prev = NULL;
	int this_call_cnt = 0;

	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
				"Subvolume %s returnd -1(%s)",
				prev->this->name, strerror(op_errno));
			goto unlock;
		}
		combine_stat_merge (this, &local->stbuf, stbuf, prev->this);
		if (local->inode)
			local->stbuf.st_ino = local->inode->ino;
		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt))
		COMBINE_STACK_UNWIND (stat, frame, 
			local->op_ret, local->op_errno, &local->stbuf);
	return 0;
}
int
combine_lookup (call_frame_t *frame, xlator_t *this,
		loc_t *loc, dict_t *xattr_req)
{
        xlator_t     *subvol = NULL;
        combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_inode_ctx_t *ictx1 = NULL, *ictx2 = NULL;
	uint64_t ictx1_int = 0, ictx2_int = 0;
        int           ret    = -1;
        int           op_errno = -1;
	int           i = 0;

        VALIDATE_OR_GOTO (frame, err);
        VALIDATE_OR_GOTO (this, err);
        VALIDATE_OR_GOTO (loc, err);
        VALIDATE_OR_GOTO (loc->inode, err);
        VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;

	local = combine_local_init (frame);
	if (!local) {
		op_errno = ENOMEM;
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		goto err;
	}

	local->inode = inode_ref (loc->inode);
	ret = loc_dup (loc, &local->loc); 
	if (ret == -1) {
		op_errno = -errno;
		gf_log (this->name, GF_LOG_DEBUG,
			"copying location failed for path %s",
			loc->path);
		goto err;
	}

	if (loc->ino == 1) {
	        local->call_cnt = conf->subvolume_cnt;
		for (i = 0; i < conf->subvolume_cnt; i++) {
	                STACK_WIND (frame, combine_lookup_cbk,
                        	conf->subvolumes[i],
			    	conf->subvolumes[i]->fops->lookup,
                            	loc, xattr_req);
		}
		return 0;
	} else {
		ret = inode_ctx_get (loc->inode, this, &ictx1_int);
		if (ret) {
			ictx1 = CALLOC (1, sizeof(combine_inode_ctx_t));
			if (ictx1 == NULL) {
				gf_log (this->name, GF_LOG_ERROR, "Out of memory");
				op_errno = ENOMEM;
				goto err;
			}
			ictx1->ino = loc->inode->ino;
			ictx1->gen = loc->inode->generation;

			if (loc->parent && !inode_ctx_get (loc->parent, this, &ictx2_int)) {
				ictx2 = (combine_inode_ctx_t *)ictx2_int;
				ictx1->position = ictx2->position;
				if (!ictx2->local)
					ictx1->local = ictx2->local;
				else
					ictx1->local = combine_is_local_loc (this, loc);
			} else {
				ictx1->position = combine_vol_by_loc (this, loc);
				if (ictx1->position == NULL) {
					gf_log (this->name, GF_LOG_ERROR,
						"get position for loc failed for path %s",
						loc->path);
					op_errno = EINVAL;
					goto err;
				}
				ictx1->local = combine_is_local_loc (this, loc);
			}
			ret = inode_ctx_put (loc->inode, this, (uint64_t)ictx1);
			if (ret) {
				gf_log (this->name, GF_LOG_ERROR, "Could not set ctx");
			}
		} else
			ictx1 = (combine_inode_ctx_t *)ictx1_int;

		local->call_cnt = 1;
		subvol = ictx1->position;
	        STACK_WIND (frame, combine_lookup_cbk,
                       	subvol,
		    	subvol->fops->lookup,
                      	loc, xattr_req);
	}
	return 0;
err:
	if (ictx1 != NULL) {
		free (ictx1);
	}
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (lookup, frame, -1, op_errno, NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
	      int op_ret, int op_errno, fd_t *fd)
{
	combine_local_t  *local = NULL;
	int           this_call_cnt = 0;
	call_frame_t *prev = NULL;

	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
				"subvolume %s returned -1 (%s)",
				prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (opendir, frame, local->op_ret, local->op_errno,
				  local->fd);
	}
        return 0;
}


int
combine_fd_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
	      int op_ret, int op_errno, fd_t *fd)
{
	combine_local_t  *local = NULL;
	int           this_call_cnt = 0;
	call_frame_t *prev = NULL;

	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
				"subvolume %s returned -1 (%s)",
				prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (open, frame, local->op_ret, local->op_errno,
				  local->fd);
	}
        return 0;
}

int
combine_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		 int op_ret, int op_errno)
{
	combine_local_t  *local = NULL;
	int           this_call_cnt = 0;
	call_frame_t *prev = NULL;

	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
					"subvolume %s returned -1 (%s)",
					prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (access, frame, local->op_ret, local->op_errno);
	}

	return 0;
}

int
combine_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		 int op_ret, int op_errno, struct stat *preparent,
		 struct stat *postparent)
{
	combine_local_t  *local = NULL;
	int           this_call_cnt = 0;
	call_frame_t *prev = NULL;


	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
				"subvolume %s returned -1 (%s)",
				prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (unlink, frame, 
			local->op_ret, local->op_errno, NULL, NULL);
	}

	return 0;
}

int
combine_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		 int op_ret, int op_errno, struct stat *preparent,
		 struct stat *postparent)
{
	combine_local_t  *local = NULL;
	int           this_call_cnt = 0;
	call_frame_t *prev = NULL;


	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
				"subvolume %s returned -1 (%s)",
				prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (rmdir, frame, 
			local->op_ret, local->op_errno, NULL, NULL);
	}

	return 0;
}
int
combine_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
		int op_errno, gf_dirent_t *orig_entries)
{
	xlator_t     *next_subvol = NULL;
	combine_local_t *local = NULL;
	gf_dirent_t entries;
	gf_dirent_t *orig_entry = NULL;
	gf_dirent_t  *entry = NULL;
	call_frame_t *prev = NULL;
	off_t         next_offset = 0;
	int           count = 0;

	INIT_LIST_HEAD (&entries.list);
	prev = cookie;
	local = frame->local;

	if (op_ret < 0)
		goto done;

	list_for_each_entry (orig_entry, (&orig_entries->list), list) {
		next_offset = orig_entry->d_off;

		entry = gf_dirent_for_name (orig_entry->d_name);
		if (!entry) {
			gf_log (this->name, GF_LOG_ERROR,
					"Out of memory");
			goto unwind;
		}

		entry->d_stat = orig_entry->d_stat;

		combine_itransform (this, prev->this, orig_entry->d_ino,
				&entry->d_ino);
		combine_itransform (this, prev->this, orig_entry->d_off,
				&entry->d_off);

		entry->d_stat.st_ino = entry->d_ino;
		entry->d_type = orig_entry->d_type;
		entry->d_len  = orig_entry->d_len;

		list_add_tail (&entry->list, &entries.list);
		count ++;
	}
	op_ret = count;
done:

	if ((local->fd != NULL) && (local->fd->inode->ino == 1) 
			&& (op_ret == 0)){
		/*
		 * root directory, in practise, this impossilbe
		 * to happen
		 */
		/* non-zero next_offset means that
		 * EOF is not yet hit on the current subvol
		 *                                    */
		if (next_offset == 0) {
			next_subvol = combine_subvol_next (this, prev->this);
		} else {
			next_subvol = prev->this;
		}

		if (!next_subvol) {
			goto unwind;
		}

		STACK_WIND (frame, combine_readdirp_cbk,
				next_subvol, next_subvol->fops->readdir,
				local->fd, local->size, next_offset);
		return 0;
	}
unwind:
	if (op_ret < 0)
		op_ret = 0;

	COMBINE_STACK_UNWIND (readdir, frame, op_ret, op_errno, &entries);

	gf_dirent_free (&entries);
	return 0;
}

int
combine_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret,
		int op_errno, gf_dirent_t *orig_entries)
{
	xlator_t     *next_subvol = NULL;
	combine_local_t *local = NULL;
	gf_dirent_t entries;
	gf_dirent_t *orig_entry = NULL;
	gf_dirent_t  *entry = NULL;
	call_frame_t *prev = NULL;
	off_t         next_offset = 0;
	int           count = 0;

	INIT_LIST_HEAD (&entries.list);
	prev = cookie;
	local = frame->local;

	if (op_ret < 0)
		goto done;

	list_for_each_entry (orig_entry, (&orig_entries->list), list) {
		next_offset = orig_entry->d_off;

		entry = gf_dirent_for_name (orig_entry->d_name);
		if (!entry) {
			gf_log (this->name, GF_LOG_ERROR,
					"Out of memory");
			goto unwind;
		}
		combine_itransform (this, prev->this, orig_entry->d_ino,
				&entry->d_ino);
		combine_itransform (this, prev->this, orig_entry->d_off,
				&entry->d_off);

		entry->d_type = orig_entry->d_type;
		entry->d_len  = orig_entry->d_len;

		list_add_tail (&entry->list, &entries.list);
	}
	op_ret = count;
done:
	if ((local->fd != NULL) && (local->fd->inode->ino = 0)
			&& (op_ret == 0)){
		/*
		 * root directory, in practise, this impossilbe
		 * to happen
		 */
		/* non-zero next_offset means that
		 * EOF is not yet hit on the current subvol
		 *                                    */
		if (next_offset == 0) {
			next_subvol = combine_subvol_next (this, prev->this);
		} else {
			next_subvol = prev->this;
		}

		if (!next_subvol) {
			goto unwind;
		}

		STACK_WIND (frame, combine_readdir_cbk,
				next_subvol, next_subvol->fops->readdir,
				local->fd, local->size, next_offset);
		return 0;
	}
unwind:
	if (op_ret < 0)
		op_ret = 0;

	COMBINE_STACK_UNWIND (readdir, frame, op_ret, op_errno, &entries);

	gf_dirent_free (&entries);
	return 0;
}
	int
combine_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		int op_ret, int op_errno,
		struct iovec *vector, int count, struct stat *stbuf,
		struct iobref *iobref)
{
	COMBINE_STACK_UNWIND (readv, frame, op_ret, op_errno, vector, count, stbuf,
			iobref);

	return 0;
}

int
combine_flush_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
		 int op_ret, int op_errno)
{
	combine_local_t  *local = NULL;
	int this_call_cnt = 0;
	call_frame_t *prev = NULL;


	local = frame->local;
	prev = cookie;

	LOCK (&frame->lock);
	{
		if (op_ret == -1) {
			local->op_errno = op_errno;
			gf_log (this->name, GF_LOG_DEBUG,
					"subvolume %s returned -1 (%s)",
					prev->this->name, strerror (op_errno));
			goto unlock;
		}

		local->op_ret = 0;
	}
unlock:
	UNLOCK (&frame->lock);

	this_call_cnt = combine_frame_return (frame);
	if (is_last_call (this_call_cnt)) {
		COMBINE_STACK_UNWIND (flush, frame, local->op_ret, local->op_errno);
	}

	return 0;
}

int
combine_stat (call_frame_t *frame, xlator_t *this,
		loc_t *loc)
{
	xlator_t     *subvol = NULL;
	combine_conf_t *conf = NULL;
	combine_local_t  *local = NULL;
	combine_inode_ctx_t *ictx = NULL;
	uint64_t ictx_int = 0;
	int           op_errno = -1, i = 0;

	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (loc, err);
	VALIDATE_OR_GOTO (loc->inode, err);
	VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;
	local = combine_local_init (frame);
	if (local == NULL){
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = ENOMEM;
		goto err;
	}
	local->inode = inode_ref (loc->inode);

	if (loc->inode->ino == 1) {
		local->call_cnt = conf->subvolume_cnt;
		for (i = 0; i < conf->subvolume_cnt; i++){
			STACK_WIND (frame, combine_attr_cbk,
					conf->subvolumes[i],
					conf->subvolumes[i]->fops->stat,
					loc);
		}
	} else {
		if (!inode_ctx_get (loc->inode, this, &ictx_int)) {
			ictx = (combine_inode_ctx_t *)ictx_int;
		} else {
			ictx = CALLOC (1, sizeof(combine_inode_ctx_t));
			if (ictx == NULL){
				gf_log (this->name, GF_LOG_ERROR, "Out of memory");
				op_errno = ENOMEM;
				goto err;
			}
			ictx->position = combine_vol_by_loc (this, loc);
			ictx->ino = loc->inode->ino;
			ictx->gen = loc->inode->generation;
			ictx->local = combine_is_local_loc (this, loc);
			if (inode_ctx_put (loc->inode, this, (uint64_t)ictx))
				gf_log (this->name, GF_LOG_WARNING,"Could not set inode ctx");
		}
		subvol = ictx->position;
		local->call_cnt = 1;
		STACK_WIND (frame, combine_attr_cbk,
				subvol,
				subvol->fops->stat, loc);
	}
	return 0;
err:
	if (ictx != NULL)
		free(ictx);
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (stat, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_fstat (call_frame_t *frame, xlator_t *this,
		fd_t *fd)
{
	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t *fdctx = NULL;
	xlator_t     *xvol = NULL;
	uint64_t fdctx_int = 0;
	int           op_errno = -1, i;


	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (fd, err);

	conf = this->private;
	local = combine_local_init (frame);

	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = -1;
		goto err;
	}
	local->fd = fd_ref (fd);
	local->inode = inode_ref (fd->inode);

	if (local->inode->ino == 1) {
		local->call_cnt = conf->subvolume_cnt;
		for (i = 0; i < conf->subvolume_cnt; i++){
			STACK_WIND (frame, combine_attr_cbk,
					conf->subvolumes[i],
					conf->subvolumes[i]->fops->fstat,
					fd);
		}
	} else {
		if (!fd_ctx_get (fd, this, &fdctx_int)) {
			fdctx = (combine_fd_ctx_t *)fdctx_int;
		} else {
			gf_log (this->name, GF_LOG_ERROR,
					"Could not get fd ctx");
			op_errno = EBADFD;
			goto err;
		}
		xvol = fdctx->position;
		local->call_cnt = 1;
		STACK_WIND (frame, combine_attr_cbk,
				xvol,
				xvol->fops->fstat, fd);
	}
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (fstat, frame, -1, EROFS, NULL);
	return 0;
}

int
combine_truncate (call_frame_t *frame, xlator_t *this,
		loc_t *loc, off_t offset)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (truncate, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
combine_ftruncate (call_frame_t *frame, xlator_t *this,
		fd_t *fd, off_t offset)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (ftruncate, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
combine_access (call_frame_t *frame, xlator_t *this,
		loc_t *loc, int32_t mask)
{
	xlator_t *subvol = NULL;
	combine_local_t *local = NULL;
	combine_conf_t *conf = NULL;
	combine_inode_ctx_t *ictx;
	uint64_t ictx_int = 0;
	int op_errno = -1, ret = 0;

	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (loc, err);
	VALIDATE_OR_GOTO (loc->inode, err);
	VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;
	local = combine_local_init (frame);
	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = ENOMEM;
		goto err;
	}
	/* 
	 * for root, do access on first child.
	 * for others, do access on it's position child.
	 */
	if (loc->inode->ino == 1) {
		subvol = conf->subvolumes[0];
	} else {
		ret = inode_ctx_get (loc->inode, this, &ictx_int);
		if (ret) {
			gf_log (this->name, GF_LOG_ERROR, "Could not get inode ctx");
			op_errno = -ret;
			goto err;
		}
		ictx = (combine_inode_ctx_t *)ictx_int;
		if (ictx->position == NULL)
			goto err;
		subvol = ictx->position;

	}
	local->call_cnt = 1;
	STACK_WIND (frame, combine_access_cbk,
			subvol,
			subvol->fops->access,
			loc, mask);
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (access, frame, 0, op_errno);
	return 0;
}

int
combine_readlink (call_frame_t *frame, xlator_t *this,
		loc_t *loc, size_t size)
{
	COMBINE_STACK_UNWIND (readlink, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
combine_mknod (call_frame_t *frame, xlator_t *this,
		loc_t *loc, mode_t mode, dev_t rdev)
{
	COMBINE_STACK_UNWIND (mknod, frame, -1, EROFS,
			NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_mkdir (call_frame_t *frame, xlator_t *this,
		loc_t *loc, mode_t mode)
{
	COMBINE_STACK_UNWIND (mkdir, frame, -1, EROFS,
			NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_unlink (call_frame_t *frame, xlator_t *this,
		loc_t *loc)
{
	xlator_t *subvol = NULL;
	combine_local_t *local = NULL;
	combine_conf_t *conf = NULL;
	combine_inode_ctx_t *ictx;
	uint64_t ictx_int = 0;
	int op_errno = -1, ret = 0;

	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (loc, err);
	VALIDATE_OR_GOTO (loc->inode, err);
	VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;
	local = combine_local_init (frame);
	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = ENOMEM;
		goto err;
	}
	gf_log (this->name, GF_LOG_TRACE, "Unlink file %s, ino %"PRId64"",
			loc->path, loc->inode->ino);

	ret = inode_ctx_get (loc->inode, this, &ictx_int);
	if (ret) {
		subvol = combine_vol_by_loc (this, loc);
	} else {
		ictx = (combine_inode_ctx_t *)ictx_int;
		subvol = ictx->position;
	}

	if (subvol == NULL)
		goto err;

	local->call_cnt = 1;
	STACK_WIND (frame, combine_unlink_cbk,
			subvol,
			subvol->fops->unlink,
			loc);
	return 0;
err:
	op_errno = (op_errno == -1)?errno:op_errno;
	COMBINE_STACK_UNWIND (unlink, frame, -1, op_errno, NULL, NULL);
	return 0;
}

int
combine_rmdir (call_frame_t *frame, xlator_t *this,
		loc_t *loc)
{
	/* TODO:
	 * remove directory using this way poor efficency.
	 * provide a new way to deleting directory by ioctl
	 */
	xlator_t *subvol = NULL;
	combine_local_t *local = NULL;
	combine_conf_t *conf = NULL;
	combine_inode_ctx_t *ictx;
	uint64_t ictx_int = 0;
	int op_errno = -1, ret = 0;

	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (loc, err);
	VALIDATE_OR_GOTO (loc->inode, err);
	VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;
	local = combine_local_init (frame);
	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = ENOMEM;
		goto err;
	}

	ret = inode_ctx_get (loc->inode, this, &ictx_int);
	if (ret) {
		subvol = combine_vol_by_loc (this, loc);
	} else {
		ictx = (combine_inode_ctx_t *)ictx_int;
		subvol = ictx->position;
	}

	if (subvol == NULL)
		goto err;

	gf_log (this->name, GF_LOG_TRACE, "rmdir dir %s, ino %"PRId64" in subvol[%s], local %d",
		loc->path, loc->inode->ino, subvol->name, ictx->local);

	if (ictx->local) {
		op_errno = ENOSYS;
		goto err;
	}

	local->call_cnt = 1;
	STACK_WIND (frame, combine_rmdir_cbk,
		subvol,
		subvol->fops->rmdir,
		loc);
	return 0;
err:
	op_errno = (op_errno == -1)?errno:op_errno;
	COMBINE_STACK_UNWIND (rmdir, frame, -1, op_errno, NULL, NULL);
	return 0;
}

int
combine_symlink (call_frame_t *frame, xlator_t *this,
		const char *linkname, loc_t *loc)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (symlink, frame, -1, EROFS,
		NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_rename (call_frame_t *frame, xlator_t *this,
		loc_t *oldloc, loc_t *newloc)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (rename, frame, -1, EROFS,
		NULL, NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_link (call_frame_t *frame, xlator_t *this,
	       loc_t *oldloc, loc_t *newloc)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (link, frame, -1, EROFS,
		NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_create (call_frame_t *frame, xlator_t *this,
		loc_t *loc, int32_t flags, mode_t mode, fd_t *fd)
{
	COMBINE_STACK_UNWIND (create, frame, -1, EROFS,
		NULL, NULL, NULL, NULL, NULL);
	return 0;
}

int
combine_open (call_frame_t *frame, xlator_t *this,
		loc_t *loc, int32_t flags, fd_t *fd, int32_t wbflags)
{
	xlator_t *subvol = NULL;
	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t *fdctx = NULL;
	combine_inode_ctx_t *ictx = NULL;
	uint64_t ictx_int = 0;
	int           ret = -1;
        int           op_errno = -1;


        VALIDATE_OR_GOTO (frame, err);
        VALIDATE_OR_GOTO (this, err);
        VALIDATE_OR_GOTO (fd, err);
	conf = this->private;

	local = combine_local_init (frame);
	if (local == NULL) {
		op_errno = ENOMEM;
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		goto err;
	}

	local->fd = fd_ref (fd);
	ret = loc_dup (loc, &local->loc);
	if (ret == -1) {
		op_errno = ENOMEM;
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		goto err;
	}

	gf_log (this->name, GF_LOG_TRACE, "open for path %s,inode %"PRId64", fd %p, flags %d, wbflags %d",
		loc->path, fd->inode->ino, fd, flags, wbflags);

	if ((flags & O_WRONLY) || (flags & O_WRONLY)) {
		gf_log (this->name, GF_LOG_WARNING, "read only file system");
		op_errno = EROFS;
		goto err;
	}
	fdctx = CALLOC (1, sizeof(combine_fd_ctx_t));
	if (fdctx == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		goto err;
	}

	ret = inode_ctx_get (loc->inode, this, &ictx_int);
	if (ret) {
		/* TODO : here got position by loc */
		gf_log (this->name, GF_LOG_WARNING, "Could not get ctx from inode %"PRId64"",
			loc->inode->ino);
		fdctx->position = combine_vol_by_loc (this, loc);
		op_errno = -ret;
		goto err;
	} else {
		ictx = (combine_inode_ctx_t *)ictx_int;
		fdctx->position = ictx->position;
	}
	fdctx->ino = loc->inode->ino;
	fdctx->gen = loc->inode->generation;
	/*
	 * TODO: OTHER FIELD to fill.
	 */
	ret = fd_ctx_set (fd, this, (uint64_t)fdctx);
	if (ret) {
		gf_log (this->name, GF_LOG_ERROR, "could not set fd ctx");
		op_errno = EIO;
		goto err;
	}
	subvol = fdctx->position;
	local->call_cnt = 1;

	STACK_WIND (frame, combine_fd_cbk,
		subvol,
		subvol->fops->open,
		loc, flags, fd, wbflags);
	return 0;
err:
	if (fdctx != NULL)
		free (fdctx);

	op_errno = (op_errno == -1)?errno:op_errno;
	COMBINE_STACK_UNWIND (open, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_readv (call_frame_t *frame,	xlator_t *this,
		fd_t *fd, size_t size, off_t offset)
{
	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t *fdctx = NULL;
	xlator_t     *xvol = NULL;
	uint64_t fdctx_int = 0;
	int           op_errno = -1, ret = 0;


	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (fd, err);

	conf = this->private;
	local = combine_local_init (frame);

       	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = -1;
		goto err;
	}
	local->fd = fd_ref (fd);
	local->size = size;

	gf_log (this->name, GF_LOG_TRACE, "read for inode %p[%"PRId64"]fd %p size %d offset %d",
			fd->inode, fd->inode->ino, fd, size, offset);

	ret = fd_ctx_get (fd, this, &fdctx_int);
	if (!ret) {
		fdctx = (combine_fd_ctx_t *)fdctx_int;
	} else {
		gf_log (this->name, GF_LOG_ERROR,
				"Could not get fd ctx");
		op_errno = -ret;
		goto err;
	}
	xvol = fdctx->position;
	local->call_cnt = 1;

	STACK_WIND (frame, combine_readv_cbk,
		xvol, xvol->fops->readv,
		fd, size, offset);
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (readdir, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_writev (call_frame_t *frame, xlator_t *this,
		fd_t *fd, struct iovec *vector,	int32_t count,
		off_t offset, struct iobref *iobref)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (writev, frame, -1, EROFS, NULL, NULL);

	return 0;
}

int
combine_flush (call_frame_t *frame, xlator_t *this,
		fd_t *fd)
{
	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t *fdctx = NULL;
	xlator_t     *xvol = NULL;
	uint64_t fdctx_int = 0;
	int           op_errno = -1, ret = 0;


	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (fd, err);

	conf = this->private;
	local = combine_local_init (frame);

       	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = -1;
		goto err;
	}
	local->fd = fd_ref (fd);

	gf_log (this->name, GF_LOG_TRACE, "flush for inode %p[%"PRId64"]fd %p",
			fd->inode, fd->inode->ino, fd);

	ret = fd_ctx_get (fd, this, &fdctx_int);
	if (!ret) {
		fdctx = (combine_fd_ctx_t *)fdctx_int;
	} else {
		gf_log (this->name, GF_LOG_ERROR,
				"Could not get fd ctx");
		op_errno = -ret;
		goto err;
	}
	xvol = fdctx->position;
	local->call_cnt = 1;

	STACK_WIND (frame, combine_flush_cbk,
		xvol, xvol->fops->flush, fd);
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (flush, frame, -1, op_errno);
	return 0;
}

int 
combine_fsync (call_frame_t *frame, xlator_t *this,
		fd_t *fd, int32_t datasync)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fsync, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
combine_opendir (call_frame_t *frame, xlator_t *this,
		loc_t *loc, fd_t *fd)
{
	xlator_t *subvol = NULL;
	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t      *fdctx = NULL;
	combine_inode_ctx_t *ictx = NULL;
	uint64_t ictx_int = 0;
	int           ret = -1;
        int           op_errno = -1, i = 0;

        VALIDATE_OR_GOTO (frame, err);
        VALIDATE_OR_GOTO (this, err);
        VALIDATE_OR_GOTO (fd, err);
	conf = this->private;

	local = combine_local_init (frame);
	if (local == NULL) {
		op_errno = ENOMEM;
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		goto err;
	}

	local->fd = fd_ref (fd);
	ret = loc_dup (loc, &local->loc);
	if (ret == -1) {
		gf_log (this->name, GF_LOG_ERROR,
			"Out of memory");
		op_errno = ENOMEM;
		goto err;
	}

	gf_log (this->name, GF_LOG_TRACE, "opendir for path %s,inode %"PRId64", fd %p",
			loc->path, fd->inode->ino, fd);
	/*
	 * for root, opendir on every subvol.
	 * for non-root, if we can get ictx from parent or
	 * from inode, then using ctx to determine which
	 * subvol to go on, else we compute which subvol
	 * to go, and set inode ctx, fd ctx.
	 */
	if (loc->inode->ino == 1) {
		local->call_cnt = conf->subvolume_cnt;

		for (i = 0; i < conf->subvolume_cnt; i++) {
			STACK_WIND (frame, combine_opendir_cbk,
			    conf->subvolumes[i],
			    conf->subvolumes[i]->fops->opendir,
			    loc, fd);
		}
	} else {
		fdctx = CALLOC (1, sizeof(combine_fd_ctx_t));
		if (fdctx == NULL) {
			gf_log (this->name, GF_LOG_ERROR, "Out of memory");
			goto err;
		}

		ret = inode_ctx_get (loc->inode, this, &ictx_int);
		if (ret) {
			gf_log (this->name, GF_LOG_WARNING, "Could not get ctx from inode %"PRId64"",
				loc->inode->ino);
			fdctx->position = combine_vol_by_loc (this, loc);
		} else {
			ictx = (combine_inode_ctx_t *)ictx_int;
			fdctx->position = ictx->position;
		}

		fdctx->ino = loc->inode->ino;
		fdctx->gen = loc->inode->generation;
		/*
		 * TODO: OTHER FIELD to fill.
		 */
		ret = fd_ctx_set (fd, this, (uint64_t)fdctx);
		if (ret) {
			gf_log (this->name, GF_LOG_ERROR, "could not set fd ctx");
			op_errno = EIO;
			goto err;
		}
		subvol = fdctx->position;
		local->call_cnt = 1;

	        STACK_WIND (frame, combine_opendir_cbk,
                       	subvol,
		    	subvol->fops->opendir,
                      	loc, fd);
	}
	return 0;
err:
	if (fdctx != NULL) {
		free (fdctx);
	}
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (opendir, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_fsyncdir (call_frame_t *frame, xlator_t *this,
		fd_t *fd, int32_t datasync)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fsyncdir, frame, -1, EROFS);
	return 0;
}

int
combine_statfs (call_frame_t *frame, xlator_t *this,
		loc_t *loc)
{
	combine_local_t *local = NULL;
	combine_conf_t *conf = NULL;
	int op_errno = -1;
	int i = -1;

        VALIDATE_OR_GOTO (frame, err);
        VALIDATE_OR_GOTO (this, err);
        VALIDATE_OR_GOTO (loc, err);
        VALIDATE_OR_GOTO (loc->inode, err);
        VALIDATE_OR_GOTO (loc->path, err);

	conf = this->private;

	local = combine_local_init (frame);
	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = -1;
		goto err;
	}
	local->call_cnt = conf->subvolume_cnt;

	gf_log (this->name, GF_LOG_TRACE, "statfs of path %s", loc->path);

	for (i = 0; i < conf->subvolume_cnt; i++) {
		STACK_WIND (frame, combine_statfs_cbk,
			conf->subvolumes[i],
			conf->subvolumes[i]->fops->statfs, loc);
	}
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (statfs, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_setxattr (call_frame_t *frame, xlator_t *this,
		loc_t *loc, dict_t *dict, int32_t flags)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (setxattr, frame, -1, EROFS);
	return 0;
}

int
combine_getxattr (call_frame_t *frame, xlator_t *this,
		loc_t *loc, const char *name)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (getxattr, frame, -1, ENOSYS, NULL);
	return 0;
}

int
combine_fsetxattr (call_frame_t *frame, xlator_t *this,
                fd_t *fd, dict_t *dict, int32_t flags)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fsetxattr, frame, -1, EROFS);
	return 0;
}

int
combine_fgetxattr (call_frame_t *frame, xlator_t *this,
                fd_t *fd, const char *name)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fgetxattr, frame, -1, EROFS, NULL);
	return 0;
}

int
combine_removexattr (call_frame_t *frame, xlator_t *this,
		loc_t *loc, const char *name)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (removexattr, frame, -1, EROFS);
	return 0;
}

int
combine_lk (call_frame_t *frame, xlator_t *this,
		fd_t *fd, int32_t cmd, struct flock *flock)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (lk, frame, -1, EROFS, NULL);
	return 0;
}

int
combine_inodelk (call_frame_t *frame, xlator_t *this,
                const char *volume, loc_t *loc,
		int32_t cmd, struct flock *flock)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (inodelk, frame, -1, EROFS);
	return 0;
}

int
combine_finodelk (call_frame_t *frame, xlator_t *this,
		const char *volume, fd_t *fd,
		int32_t cmd, struct flock *flock)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (finodelk, frame, -1, EROFS);
	return 0;
}

int
combine_entrylk (call_frame_t *frame, xlator_t *this, 
		const char *volume, loc_t *loc, const char *basename,
		entrylk_cmd cmd, entrylk_type type)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (entrylk, frame, -1, EROFS);
	return 0;
}

int
combine_fentrylk (call_frame_t *frame, xlator_t *this, 
                const char *volume, fd_t *fd,
		const char *basename, entrylk_cmd cmd,
		entrylk_type type)

{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fentrylk, frame, -1, EROFS);
	return 0;
}

int
combine_do_readdir (call_frame_t *frame, xlator_t *this,
		fd_t *fd, size_t size, off_t offset, int whichop)
{

	combine_local_t  *local  = NULL;
	combine_conf_t   *conf = NULL;
	combine_fd_ctx_t *fdctx = NULL;
	xlator_t     *xvol = NULL;
	off_t         xoff = 0;
	uint64_t fdctx_int = 0;
	int           op_errno = -1, ret = 0;


	VALIDATE_OR_GOTO (frame, err);
	VALIDATE_OR_GOTO (this, err);
	VALIDATE_OR_GOTO (fd, err);

	conf = this->private;
	local = combine_local_init (frame);

       	if (local == NULL) {
		gf_log (this->name, GF_LOG_ERROR, "Out of memory");
		op_errno = -1;
		goto err;
	}
	local->fd = fd_ref (fd);
	local->size = size;

	gf_log (this->name, GF_LOG_TRACE, "readdir for inode %p[%"PRId64"]fd %p size %d offset %d",
			fd->inode, fd->inode->ino, fd, size, xoff);

	if (fd->inode->ino == 1) {
		combine_deitransform (this, offset, &xvol, (uint64_t *)&xoff);
		if (whichop == GF_FOP_READDIRP)
		       	STACK_WIND (frame, combine_readdirp_cbk,
				xvol, xvol->fops->readdirp,
				fd, size, xoff);
		else
		       	STACK_WIND (frame, combine_readdir_cbk,
				xvol, xvol->fops->readdir,
				fd, size, xoff);
		
	} else {
		ret = fd_ctx_get (fd, this, &fdctx_int);
		if (!ret) {
			fdctx = (combine_fd_ctx_t *)fdctx_int;
		} else {
			gf_log (this->name, GF_LOG_ERROR,
				"Could not get fd ctx");
			op_errno = -ret;
			goto err;
		}
		xvol = fdctx->position;
		local->call_cnt = conf->subvolume_cnt;

		if (whichop == GF_FOP_READDIRP)
			STACK_WIND (frame, combine_readdirp_cbk,
				xvol, xvol->fops->readdir,
				fd, size, offset);
		else
			STACK_WIND (frame, combine_readdir_cbk,
				xvol, xvol->fops->readdir,
				fd, size, offset);
	}
	return 0;
err:
	op_errno = (op_errno == -1) ? errno : op_errno;
	COMBINE_STACK_UNWIND (readdir, frame, -1, op_errno, NULL);
	return 0;
}

int
combine_readdir (call_frame_t *frame, xlator_t *this,
		fd_t *fd, size_t size, off_t offset)
{
	combine_do_readdir (frame, this, fd, size, offset, GF_FOP_READDIR);
	return 0;
}

int
combine_readdirp (call_frame_t *frame, xlator_t *this,
		fd_t *fd, size_t size, off_t offset)
{
	combine_do_readdir (frame, this, fd, size, offset, GF_FOP_READDIRP);
	return 0;
}

int
combine_xattrop (call_frame_t *frame, xlator_t *this,
		loc_t *loc, gf_xattrop_flags_t optype,	dict_t *xattr)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (xattrop, frame, -1, EROFS, NULL);
	return 0;
}

int
combine_fxattrop (call_frame_t *frame, xlator_t *this,
		fd_t *fd, gf_xattrop_flags_t optype, dict_t *xattr)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fxattrop, frame, -1, EROFS, NULL);
	return 0;
}

int
combine_ioctl (call_frame_t *frame, xlator_t *this, fd_t *fd,
		uint32_t cmd, uint64_t arg)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (ioctl, frame, -1, EROFS, 0, (uint64_t)NULL);
	return 0;
}

int
combine_setattr (call_frame_t *frame, xlator_t *this,
		loc_t *loc, struct stat *stbuf, int32_t valid)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (setattr, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
combine_fsetattr (call_frame_t *frame, xlator_t *this,
                fd_t *fd, struct stat *stbuf, int32_t valid)
{
	/* NOSYS */
	COMBINE_STACK_UNWIND (fsetattr, frame, -1, EROFS, NULL, NULL);
	return 0;
}

int
notify (xlator_t *this, int event, void *data, ...)
{
	xlator_t   *subvol = NULL;
	int         cnt    = -1;
	int         i      = -1;
	combine_conf_t *conf   = NULL;
	int         ret    = -1;


	conf = this->private;

	switch (event) {
	case GF_EVENT_CHILD_UP:
		subvol = data;

		for (i = 0; i < conf->subvolume_cnt; i++) {
			if (subvol == conf->subvolumes[i]) {
				cnt = i;
				break;
			}
		}

		if (cnt == -1) {
			gf_log (this->name, GF_LOG_DEBUG,
				"got GF_EVENT_CHILD_UP bad subvolume %s",
				subvol->name);
			break;
		}

		LOCK (&conf->subvolume_lock);
		{
			conf->subvolume_status[cnt] = 1;
		}
		UNLOCK (&conf->subvolume_lock);

		break;

	case GF_EVENT_CHILD_DOWN:
		subvol = data;

		for (i = 0; i < conf->subvolume_cnt; i++) {
			if (subvol == conf->subvolumes[i]) {
				cnt = i;
				break;
			}
		}

		if (cnt == -1) {
			gf_log (this->name, GF_LOG_DEBUG,
				"got GF_EVENT_CHILD_DOWN bad subvolume %s",
				subvol->name);
			break;
		}

		LOCK (&conf->subvolume_lock);
		{
			conf->subvolume_status[cnt] = 0;
		}
		UNLOCK (&conf->subvolume_lock);

		break;
	}

	ret = default_notify (this, event, data);

	return ret;
}
void
fini (xlator_t *this)
{
        combine_conf_t *conf = NULL;

        conf = this->private;

        if (conf) {

                if (conf->subvolumes)
                        FREE (conf->subvolumes);

                if (conf->subvolume_status)
                        FREE (conf->subvolume_status);

                FREE (conf);
        }

        return;
}

int
init (xlator_t *this)
{
        combine_conf_t    *conf = NULL;
        int            ret = -1;

	if (!this->children) {
		gf_log (this->name, GF_LOG_CRITICAL,
			"Distribute needs more than one subvolume");
		return -1;
	}
  
	if (!this->parents) {
		gf_log (this->name, GF_LOG_WARNING,
			"dangling volume. check volfile");
	}

        conf = CALLOC (1, sizeof (*conf));
        if (!conf) {
                gf_log (this->name, GF_LOG_ERROR,
                        "Out of memory");
                goto err;
        }

        ret = combine_init_subvolumes (this, conf);
        if (ret == -1) {
                goto err;
        }

	LOCK_INIT (&conf->subvolume_lock);
        this->private = conf;

        return 0;

err:
        if (conf) {

                if (conf->subvolumes)
                        FREE (conf->subvolumes);
		if (conf->subvolume_status)
			FREE (conf->subvolume_status);
                FREE (conf);
        }
        return -1;
}

struct xlator_fops fops = {
	.lookup      = combine_lookup,
	.mknod       = combine_mknod,
	.create      = combine_create,
	.stat        = combine_stat,
	.fstat       = combine_fstat,
	.truncate    = combine_truncate,
	.ftruncate   = combine_ftruncate,
	.access      = combine_access,
	.readlink    = combine_readlink,
	.setxattr    = combine_setxattr,
	.getxattr    = combine_getxattr,
	.removexattr = combine_removexattr,
	.open        = combine_open,
	.readv       = combine_readv,
	.writev      = combine_writev,
	.flush       = combine_flush,
	.fsync       = combine_fsync,
	.statfs      = combine_statfs,
	.lk          = combine_lk,
	.opendir     = combine_opendir,
	.readdir     = combine_readdir,
	.readdirp    = combine_readdirp,
	.fsyncdir    = combine_fsyncdir,
	.symlink     = combine_symlink,
	.unlink      = combine_unlink,
	.link        = combine_link,
	.mkdir       = combine_mkdir,
	.rmdir       = combine_rmdir,
	.rename      = combine_rename,
	.inodelk     = combine_inodelk,
	.finodelk    = combine_finodelk,
	.entrylk     = combine_entrylk,
	.fentrylk    = combine_fentrylk,
	.xattrop     = combine_xattrop,
	.fxattrop    = combine_fxattrop,
        .setattr     = combine_setattr, 
        .fsetattr    = combine_fsetattr,
};

struct xlator_mops mops = {
};

struct xlator_dumpops dumpops = {
};


struct xlator_cbks cbks = {
};


struct volume_options options[] = {
	{ .key  = {NULL} },
};

